package class

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
)

/**
*类型管理工具类，包括entity dto qto
 */
type structUtil struct {
	allList    map[string]MyClass
	entityList map[string]MyClass
	dtoList    map[string]MyClass
	qtoList    map[string]MyClass
}

// GetTypeName 获取类型名称
// 保护包名  model.user
// 返回string
func (p *structUtil) GetTypeName(t reflect.Type) string {
	if t.Kind() == reflect.Ptr {
		return t.Elem().String()
	}
	return t.String()
}

// GetStructTypeName 获取类型名称
// 保护包名  model.user
func (p *structUtil) GetStructTypeName(i interface{}) string {
	return p.GetTypeName(reflect.TypeOf(i))
}

func (p *structUtil) SimpleCopyProperties(src, dst interface{}) (err error) {
	// 防止意外panic
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprintf("%v", e))
		}
	}()
	dstType, dstValue := reflect.TypeOf(dst), reflect.ValueOf(dst)
	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)

	// dst必须结构体指针类型
	if dstType.Kind() != reflect.Ptr || dstType.Elem().Kind() != reflect.Struct {
		return errors.New("dst type should be a struct pointer")
	}

	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		return errors.New("src type should be a struct or a struct pointer")
	}

	// 取具体内容
	dstType, dstValue = dstType.Elem(), dstValue.Elem()

	// 属性个数
	propertyNums := dstType.NumField()

	for i := 0; i < propertyNums; i++ {
		// 属性
		property := dstType.Field(i)
		// 待填充属性值
		propertyValue := srcValue.FieldByName(property.Name)

		// 无效，说明src没有这个属性 || 属性同名但类型不同
		if !propertyValue.IsValid() || property.Type != propertyValue.Type() {
			fmt.Println("fail", propertyValue)
			continue
		}
		fmt.Println("ok:", property.Name, ":", propertyValue)
		if dstValue.Field(i).CanSet() {
			dstValue.Field(i).Set(propertyValue)
		}
	}

	return nil
}

func (p *structUtil) Trim(src interface{}) (err error) {
	// 防止意外panic
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprintf("%v", e))
		}
	}()

	srcType, srcValue := reflect.TypeOf(src), reflect.ValueOf(src)

	// dst必须结构体指针类型
	if srcType.Kind() != reflect.Ptr || srcType.Elem().Kind() != reflect.Struct {
		return errors.New("dst type should be a struct pointer")
	}

	// src必须为结构体或者结构体指针
	if srcType.Kind() == reflect.Ptr {
		srcType, srcValue = srcType.Elem(), srcValue.Elem()
	}
	if srcType.Kind() != reflect.Struct {
		return errors.New("src type should be a struct or a struct pointer")
	}

	// 属性个数
	propertyNums := srcValue.NumField()

	for i := 0; i < propertyNums; i++ {
		// 属性
		property := srcValue.Field(i)
		if property.Type().Kind() == reflect.String {
			property.Set(reflect.ValueOf(strings.TrimSpace(property.String())))
		}
	}

	return nil
}

/*
GetFieldValue
根据字段名称获取指端值
*/
func (p *structUtil) GetFieldValue(src interface{}, fieldName string) interface{} {
	var srcValue = reflect.ValueOf(src)
	// dst必须结构体指针类型
	if srcValue.Kind() == reflect.Ptr {
		srcValue = srcValue.Elem()
	}
	var f = srcValue.FieldByName(fieldName)
	if f.IsValid() {
		return f.Interface()
	} else {
		return nil
	}
}

/*SetFieldValue
*根据字段名称设置值
 */
func (p *structUtil) SetFieldValue(src interface{}, fieldName string, v interface{}) {
	var srcValue = reflect.ValueOf(src)
	// dst必须结构体指针类型
	if srcValue.Kind() == reflect.Ptr {
		srcValue = srcValue.Elem()
	}
	var f = srcValue.FieldByName(fieldName)
	if f.IsValid() && f.CanSet() {
		f.Set(reflect.ValueOf(v))
	}

}

/*GetFieldIndex
*根据字段名称获取指端值
 */
func (p *structUtil) GetFieldIndex(src interface{}, fieldName string) int {
	var srcType = reflect.TypeOf(src)
	if srcType.Kind() == reflect.Ptr {
		srcType = srcType.Elem()
	}
	var num = srcType.NumField()
	for i := 0; i < num; i++ {
		if srcType.Field(i).Name == fieldName {
			return i
		}
	}
	return -1
}

/*HasField
*判断是否包含字段
 */
func (p *structUtil) HasField(src interface{}, fieldName string) bool {
	return p.GetFieldIndex(src, fieldName) >= 0
}

// 添加类
func (p *structUtil) addClass(dstMap map[string]MyClass, pcls *MyClass) (string, error) {
	var elem = pcls.Type
	if elem.Kind() == reflect.Ptr {
		elem = pcls.Type.Elem()
	}

	if pcls.Ename == "" {
		pcls.Ename = elem.Name()
	}
	if pcls.FullName == "" {
		pcls.FullName = elem.String()
	}
	pcls.Fileds = make([]MyField, 0)

	if strings.HasPrefix(pcls.FullName, "*") {
		pcls.FullName = pcls.FullName[1:]
	}
	if strings.HasPrefix(pcls.FullName, "*") {
		pcls.FullName = pcls.FullName[1:]
	}

	var _, ok = p.allList[pcls.FullName]
	if ok {
		return "", errors.New("类型已经存在")
	}

	numFields := elem.NumField() // 获取字段数量

	for i := 0; i < numFields; i++ {
		var pf = MyField{
			Ename:    elem.Field(i).Name,
			Name:     elem.Field(i).Name,
			Type:     elem.Field(i).Type,
			Lx:       p.GetTypeName(elem.Field(i).Type),
			Nullable: true,
			Index:    i,
		}
		pf.Str = pf.Lx == "string"
		pf.Date = pf.Lx == "datetime.MyTime"
		pf.Num = pf.Lx == "int64" || pf.Lx == "int"

		var validate = elem.Field(i).Tag.Get("validate")
		if "" != validate {
			arr := strings.Split(validate, ",")
			for _, maxlen := range arr {
				if strings.HasPrefix(maxlen, "required") {
					pf.Required = true
				}
				if strings.HasPrefix(maxlen, "min=") {
					var comment2 = strings.TrimSpace(maxlen)
					pf.Vmin = comment2[4:]
				}
				if strings.HasPrefix(maxlen, "max=") {
					var comment2 = strings.TrimSpace(maxlen)
					pf.Vmax = comment2[4:]
				}
				if strings.HasPrefix(maxlen, "gte=") {
					var comment2 = strings.TrimSpace(maxlen)
					pf.Vmin = comment2[3:]
				}
				if strings.HasPrefix(maxlen, "lte=") {
					var comment2 = strings.TrimSpace(maxlen)
					pf.Vmax = comment2[3:]
				}
			}
		}
		var gorm = elem.Field(i).Tag.Get("gorm")
		if gorm != "" {
			arr := strings.Split(gorm, ";")
			for _, comment := range arr {
				if strings.HasPrefix(comment, "comment:") {
					var comment2 = strings.TrimSpace(comment)
					pf.Name = comment2[8:]
					break
				}
				//if strings.HasPrefix(comment, "size:") {
				//	var comment2 = strings.TrimSpace(comment)
				//	pf.Vmax = comment2[5:]
				//	break
				//}
				if strings.HasPrefix(comment, "not null") {
					pf.Nullable = false
				}
			}
		}

		pcls.Fileds = append(pcls.Fileds, pf)
	}
	dstMap[pcls.FullName] = *pcls
	p.allList[pcls.FullName] = *pcls
	return pcls.FullName, nil
}

// AddEntity
// 添加数据库类
func (p *structUtil) AddEntity(pcls *MyClass) (string, error) {
	return p.addClass(p.entityList, pcls)
}

// AddDto
// 添加Dto类
func (p *structUtil) AddDto(pcls *MyClass) (string, error) {
	return p.addClass(p.dtoList, pcls)
}

// AddQto
// 添加Qto类
func (p *structUtil) AddQto(pcls *MyClass) (string, error) {
	return p.addClass(p.qtoList, pcls)
}

// GetClass
// 根据key返回类型
func (p *structUtil) GetClass(key string) *MyClass {
	if tpe, ok := p.allList[key]; ok {
		return &tpe
	}
	return nil
}

// FindClass
// 根据struct返回类型
func (p *structUtil) FindClass(i interface{}) *MyClass {
	var key = p.GetStructTypeName(i)
	return p.GetClass(key)
}

// Create
// 根据类型创建struct
func (p *structUtil) Create(key string) interface{} {
	if strings.HasPrefix(key, "*") {
		key = key[1:]
	}

	if mycls, ok := p.allList[key]; ok {
		tpe := mycls.Type
		value := reflect.New(tpe).Elem()
		switch tpe.Kind() {
		case reflect.Ptr:
			tValue := reflect.New(tpe.Elem()).Elem().Addr()
			value.Set(tValue)
		default:
		}
		return value.Interface()
	}
	return nil
}

// GetAllEntity
// 获取所有entity
func (p *structUtil) GetAllEntity() map[string]MyClass {
	return p.entityList
}

// GetAllDto
// 获取所有Dto
func (p *structUtil) GetAllDto() map[string]MyClass {
	return p.dtoList
}

// GetAllQto
// 获取所有Qto
func (p *structUtil) GetAllQto() map[string]MyClass {
	return p.qtoList
}

var (
	StructUtil *structUtil
)

func init() {
	StructUtil = &structUtil{
		allList:    make(map[string]MyClass),
		entityList: make(map[string]MyClass),
		dtoList:    make(map[string]MyClass),
		qtoList:    make(map[string]MyClass),
	}

}
